﻿using System;
using System.Collections.Generic;
using System.IO;

namespace ZProto
{
    public static class Generator
    {
        private static Dictionary<string, ICodeGenerator> generators = new Dictionary<string, ICodeGenerator>()
        {
            {"cs",new CsGenerator() }
        };
        private static void FileAction(string path, ICodeGenerator generator, ProtoType type, Action generate)
        {
            using var stream = File.OpenWrite(path);
            using var writer = new StreamWriter(stream);
            generator.BeginWriter(writer);
            generate();
            generator.EndWriter();
        }
        public static void Generate(string language, string input, string output, bool single)
        {
            output = Path.GetFullPath(output);
            if (generators.TryGetValue(language, out var generator))
            {
                if (single)
                {
                    GenerateMonofile(generator, input, output);
                }
                else
                {
                    GenerateMultifile(generator, input, output);
                }
            }
        }
        private static void DeleteDirectory(string path)
        {
            if (!Directory.Exists(path))
                return;
            var files = Directory.GetFiles(path);
            for (var i = 0; i < files.Length; ++i)
                File.Delete(files[i]);
            var dires = Directory.GetDirectories(path);
            for (var i = 0; i < dires.Length; ++i)
            {
                DeleteDirectory(dires[i]);
                Directory.Delete(dires[i]);
            }
        }

        private static void RecreateDirectory(string path)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
                return;
            }
            DeleteDirectory(path);
        }

        private static void GenerateMultifile(ICodeGenerator generator, string input, string output)
        {
            var proto = Parser.ParseAtPath(input);
            var typeOutput = Path.Combine(output, "Protos");
            var writerOutput = Path.Combine(output, "Writers");
            var readerOutput = Path.Combine(output, "Readers");
            RecreateDirectory(output);
            Directory.CreateDirectory(typeOutput);
            Directory.CreateDirectory(writerOutput);
            Directory.CreateDirectory(readerOutput);
            RecreateDirectory(typeOutput);
            RecreateDirectory(writerOutput);
            RecreateDirectory(readerOutput);
            for (var i = 0; i < proto.Types.Length; ++i)
            {
                var type = proto.Types[i];
                generator.BeginType(type);

                var path = Path.Combine(typeOutput, generator.TypeFileName);
                FileAction(path, generator, type, generator.GenerateProtoType);

                path = Path.Combine(writerOutput, generator.WriterFileName);
                FileAction(path, generator, type, generator.GenerateProtoWriter);

                path = Path.Combine(readerOutput, generator.ReaderFileName);
                FileAction(path, generator, type, generator.GenerateProtoReader);

                generator.EndType();
            }
        }
        private static void GenerateMonofile(ICodeGenerator generator, string input, string output)
        {
            var proto = Parser.ParseAtPath(input);
            var dirPath = Path.GetDirectoryName(output);
            if (!Directory.Exists(dirPath))
                Directory.CreateDirectory(dirPath);
            if (File.Exists(output))
                File.Delete(output);
            using var stream = File.OpenWrite(output);
            using var writer = new StreamWriter(stream);
            generator.BeginWriter(writer);
            for (var i = 0; i < proto.Types.Length; ++i)
            {
                generator.BeginType(proto.Types[i]);
                generator.GenerateProtoType();
                generator.GenerateProtoWriter();
                generator.GenerateProtoReader();
                generator.EndType();
            }
            generator.EndWriter();
        }
    }
}
